home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / mail / pine3.96.tar.gz / pine3.96.tar / pine3.96 / imap / non-ANSI / c-client / pop3.c < prev    next >
C/C++ Source or Header  |  1996-10-22  |  37KB  |  1,419 lines

  1. /*
  2.  * Program:    Post Office Protocol 3 (POP3) client routines
  3.  *
  4.  * Author:    Mark Crispin
  5.  *        Networks and Distributed Computing
  6.  *        Computing & Communications
  7.  *        University of Washington
  8.  *        Administration Building, AG-44
  9.  *        Seattle, WA  98195
  10.  *        Internet: MRC@CAC.Washington.EDU
  11.  *
  12.  * Date:    6 June 1994
  13.  * Last Edited:    22 October 1996
  14.  *
  15.  * Copyright 1996 by the University of Washington
  16.  *
  17.  *  Permission to use, copy, modify, and distribute this software and its
  18.  * documentation for any purpose and without fee is hereby granted, provided
  19.  * that the above copyright notices appear in all copies and that both the
  20.  * above copyright notices and this permission notice appear in supporting
  21.  * documentation, and that the name of the University of Washington not be
  22.  * used in advertising or publicity pertaining to distribution of the software
  23.  * without specific, written prior permission.  This software is made
  24.  * available "as is", and
  25.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  26.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  27.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  28.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  29.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
  30.  * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  31.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN
  32.  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33.  *
  34.  */
  35.  
  36.  
  37. #include "mail.h"
  38. #include "osdep.h"
  39. #include <ctype.h>
  40. #include <stdio.h>
  41. #include <errno.h>
  42. extern int errno;        /* just in case */
  43. #include "pop3.h"
  44. #include "rfc822.h"
  45. #include "misc.h"
  46.  
  47. /* POP3 mail routines */
  48.  
  49.  
  50. /* Driver dispatch used by MAIL */
  51.  
  52. DRIVER pop3driver = {
  53.   "pop3",            /* driver name */
  54.   (DRIVER *) NIL,        /* next driver */
  55.   pop3_valid,            /* mailbox is valid for us */
  56.   pop3_parameters,        /* manipulate parameters */
  57.   pop3_find,            /* find mailboxes */
  58.   pop3_find_bboards,        /* find bboards */
  59.   pop3_find_all,        /* find all mailboxes */
  60.   pop3_find_all_bboards,    /* find all bboards */
  61.   pop3_subscribe,        /* subscribe to mailbox */
  62.   pop3_unsubscribe,        /* unsubscribe from mailbox */
  63.   pop3_subscribe_bboard,    /* subscribe to bboard */
  64.   pop3_unsubscribe_bboard,    /* unsubscribe from bboard */
  65.   pop3_create,            /* create mailbox */
  66.   pop3_delete,            /* delete mailbox */
  67.   pop3_rename,            /* rename mailbox */
  68.   pop3_open,            /* open mailbox */
  69.   pop3_close,            /* close mailbox */
  70.   pop3_fetchfast,        /* fetch message "fast" attributes */
  71.   pop3_fetchflags,        /* fetch message flags */
  72.   pop3_fetchstructure,        /* fetch message envelopes */
  73.   pop3_fetchheader,        /* fetch message header only */
  74.   pop3_fetchtext,        /* fetch message body only */
  75.   pop3_fetchbody,        /* fetch message body section */
  76.   pop3_setflag,            /* set message flag */
  77.   pop3_clearflag,        /* clear message flag */
  78.   pop3_search,            /* search for message based on criteria */
  79.   pop3_ping,            /* ping mailbox to see if still alive */
  80.   pop3_check,            /* check for new messages */
  81.   pop3_expunge,            /* expunge deleted messages */
  82.   pop3_copy,            /* copy messages to another mailbox */
  83.   pop3_move,            /* move messages to another mailbox */
  84.   pop3_append,            /* append string message to mailbox */
  85.   pop3_gc            /* garbage collect stream */
  86. };
  87.  
  88.                 /* prototype stream */
  89. MAILSTREAM pop3proto = {&pop3driver};
  90.  
  91.                 /* driver parameters */
  92. static long pop3_maxlogintrials = MAXLOGINTRIALS;
  93. static long pop3_port = 0;
  94. static long pop3_loginfullname = NIL;
  95.  
  96. /* POP3 mail validate mailbox
  97.  * Accepts: mailbox name
  98.  * Returns: our driver if name is valid, NIL otherwise
  99.  */
  100.  
  101. DRIVER *pop3_valid (name)
  102.     char *name;
  103. {
  104.   DRIVER *drv = NIL;
  105.   char mbx[MAILTMPLEN];
  106.   return ((*name != '*') && (drv = mail_valid_net (name,&pop3driver,NIL,mbx))
  107.       && !strcmp (ucase (mbx),"INBOX")) ? drv : NIL;
  108. }
  109.  
  110.  
  111. /* News manipulate driver parameters
  112.  * Accepts: function code
  113.  *        function-dependent value
  114.  * Returns: function-dependent return value
  115.  */
  116.  
  117. void *pop3_parameters (function,value)
  118.     long function;
  119.     void *value;
  120. {
  121.   switch ((int) function) {
  122.   case SET_MAXLOGINTRIALS:
  123.     pop3_maxlogintrials = (long) value;
  124.     break;
  125.   case GET_MAXLOGINTRIALS:
  126.     value = (void *) pop3_maxlogintrials;
  127.     break;
  128.   case SET_POP3PORT:
  129.     pop3_port = (long) value;
  130.     break;
  131.   case GET_POP3PORT:
  132.     value = (void *) pop3_port;
  133.     break;
  134.   case SET_LOGINFULLNAME:
  135.     pop3_loginfullname = (long) value;
  136.     break;
  137.   case GET_LOGINFULLNAME:
  138.     value = (void *) pop3_loginfullname;
  139.     break;
  140.   default:
  141.     value = NIL;        /* error case */
  142.     break;
  143.   }
  144.   return value;
  145. }
  146.  
  147. /* POP3 mail find list of mailboxes
  148.  * Accepts: mail stream
  149.  *        pattern to search
  150.  */
  151.  
  152. void pop3_find (stream,pat)
  153.     MAILSTREAM *stream;
  154.     char *pat;
  155. {
  156.   /* Always a no-op */
  157. }
  158.  
  159.  
  160. /* POP3 mail find list of bboards
  161.  * Accepts: mail stream
  162.  *        pattern to search
  163.  */
  164.  
  165. void pop3_find_bboards (stream,pat)
  166.     MAILSTREAM *stream;
  167.     char *pat;
  168. {
  169.   /* Always a no-op */
  170. }
  171.  
  172. /* POP3 mail find list of all mailboxes
  173.  * Accepts: mail stream
  174.  *        pattern to search
  175.  */
  176.  
  177. void pop3_find_all (stream,pat)
  178.     MAILSTREAM *stream;
  179.     char *pat;
  180. {
  181.   char *t = NIL,tmp[MAILTMPLEN];
  182.   if (stream && LOCAL) {    /* have a mailbox stream open? */
  183.                 /* always include INBOX for consistency */
  184.     sprintf (tmp,"{%s/POP3}INBOX",LOCAL->host);
  185.     if (pmatch (((*pat == '{') && (t = strchr (pat,'}'))) ? ++t : pat,"INBOX"))
  186.       mm_mailbox (tmp);
  187.   }
  188. }
  189.  
  190.  
  191. /* POP3 mail find list of all bboards
  192.  * Accepts: mail stream
  193.  *        pattern to search
  194.  */
  195.  
  196. void pop3_find_all_bboards (stream,pat)
  197.     MAILSTREAM *stream;
  198.     char *pat;
  199. {
  200.   /* Always a no-op */
  201. }
  202.  
  203. /* POP3 mail subscribe to mailbox
  204.  * Accepts: mail stream
  205.  *        mailbox to add to subscription list
  206.  * Returns: T on success, NIL on failure
  207.  */
  208.  
  209. long pop3_subscribe (stream,mailbox)
  210.     MAILSTREAM *stream;
  211.     char *mailbox;
  212. {
  213.   return sm_subscribe (mailbox);
  214. }
  215.  
  216.  
  217. /* POP3 mail unsubscribe to mailbox
  218.  * Accepts: mail stream
  219.  *        mailbox to delete from subscription list
  220.  * Returns: T on success, NIL on failure
  221.  */
  222.  
  223. long pop3_unsubscribe (stream,mailbox)
  224.     MAILSTREAM *stream;
  225.     char *mailbox;
  226. {
  227.   return sm_unsubscribe (mailbox);
  228. }
  229.  
  230.  
  231. /* POP3 mail subscribe to bboard
  232.  * Accepts: mail stream
  233.  *        bboard to add to subscription list
  234.  * Returns: T on success, NIL on failure
  235.  */
  236.  
  237. long pop3_subscribe_bboard (stream,mailbox)
  238.     MAILSTREAM *stream;
  239.     char *mailbox;
  240. {
  241.   return NIL;            /* never valid for POP3 */
  242. }
  243.  
  244.  
  245. /* POP3 mail unsubscribe to bboard
  246.  * Accepts: mail stream
  247.  *        bboard to delete from subscription list
  248.  * Returns: T on success, NIL on failure
  249.  */
  250.  
  251. long pop3_unsubscribe_bboard (stream,mailbox)
  252.     MAILSTREAM *stream;
  253.     char *mailbox;
  254. {
  255.   return NIL;            /* never valid for POP3 */
  256. }
  257.  
  258. /* POP3 mail create mailbox
  259.  * Accepts: mail stream
  260.  *        mailbox name to create
  261.  * Returns: T on success, NIL on failure
  262.  */
  263.  
  264. long pop3_create (stream,mailbox)
  265.     MAILSTREAM *stream;
  266.     char *mailbox;
  267. {
  268.   return NIL;            /* never valid for POP3 */
  269. }
  270.  
  271.  
  272. /* POP3 mail delete mailbox
  273.  *        mailbox name to delete
  274.  * Returns: T on success, NIL on failure
  275.  */
  276.  
  277. long pop3_delete (stream,mailbox)
  278.     MAILSTREAM *stream;
  279.     char *mailbox;
  280. {
  281.   return NIL;            /* never valid for POP3 */
  282. }
  283.  
  284.  
  285. /* POP3 mail rename mailbox
  286.  * Accepts: mail stream
  287.  *        old mailbox name
  288.  *        new mailbox name
  289.  * Returns: T on success, NIL on failure
  290.  */
  291.  
  292. long pop3_rename (stream,old,new)
  293.     MAILSTREAM *stream;
  294.     char *old;
  295.     char *new;
  296. {
  297.   return NIL;            /* never valid for POP3 */
  298. }
  299.  
  300. /* POP3 mail open
  301.  * Accepts: stream to open
  302.  * Returns: stream on success, NIL on failure
  303.  */
  304.  
  305. MAILSTREAM *pop3_open (stream)
  306.     MAILSTREAM *stream;
  307. {
  308.   long i,nmsgs;
  309.   char *s,tmp[MAILTMPLEN],usrnam[MAILTMPLEN],pwd[MAILTMPLEN];
  310.   NETMBX mb;
  311.   MESSAGECACHE *elt;
  312.   struct hostent *host_name;
  313.                 /* return prototype for OP_PROTOTYPE call */
  314.   if (!stream) return &pop3proto;
  315.   mail_valid_net_parse (stream->mailbox,&mb);
  316.   if (LOCAL) {            /* if recycle stream */
  317.     sprintf (tmp,"Closing connection to %s",LOCAL->host);
  318.     if (!stream->silent) mm_log (tmp,(long) NIL);
  319.     pop3_close (stream);    /* do close action */
  320.     stream->dtb = &pop3driver;    /* reattach this driver */
  321.     mail_free_cache (stream);    /* clean up cache */
  322.   }
  323.                 /* in case /debug switch given */
  324.   if (mb.dbgflag) stream->debug = T;
  325.                 /* set up host with port override */
  326.   if (mb.port || pop3_port) sprintf (s = tmp,"%s:%ld",mb.host,
  327.                     mb.port ? mb.port : pop3_port);
  328.   else s = mb.host;        /* simple host name */
  329.   stream->local = fs_get (sizeof (POP3LOCAL));
  330.   LOCAL->host = cpystr (mb.host);
  331.   stream->sequence++;        /* bump sequence number */
  332.   LOCAL->response = LOCAL->reply = LOCAL->buf = NIL;
  333.   LOCAL->header = LOCAL->body = NIL;
  334.  
  335.                 /* try to open connection */
  336.   if (!((LOCAL->tcpstream = tcp_open (s,"pop3",POP3TCPPORT)) &&
  337.     pop3_reply (stream))) {
  338.     if (LOCAL->reply) mm_log (LOCAL->reply,ERROR);
  339.     pop3_close (stream);    /* failed, clean up */
  340.   }
  341.   else {            /* got connection */
  342.     mm_log (LOCAL->reply,NIL);    /* give greeting */
  343.                 /* only so many tries to login */
  344.     for (i = 0; i < pop3_maxlogintrials; ++i) {
  345.       *pwd = 0;            /* get password */
  346.       mm_login (pop3_loginfullname ? stream->mailbox :
  347.         tcp_host (LOCAL->tcpstream),usrnam,pwd,i);
  348.                 /* abort if he refuses to give a password */
  349.       if (*pwd == '\0') i = pop3_maxlogintrials;
  350.       else {            /* send login sequence */
  351.     if (pop3_send (stream,"USER",usrnam) && pop3_send (stream,"PASS",pwd))
  352.       break;        /* login successful */
  353.                 /* output failure and try again */
  354.     mm_log (LOCAL->reply,WARN);
  355.       }
  356.     }
  357.                 /* give up if too many failures */
  358.     if (i >=  pop3_maxlogintrials) {
  359.       mm_log (*pwd ? "Too many login failures":"Login aborted",ERROR);
  360.       pop3_close (stream);
  361.     }
  362.     else if (pop3_send (stream,"STAT",NIL)) {
  363.       LOCAL->buf = (char *) fs_get ((LOCAL->buflen = MAXMESSAGESIZE) + 1);
  364.       nmsgs = strtol (LOCAL->reply,NIL,10);
  365.                 /* create caches */
  366.       LOCAL->header = (char **) fs_get (nmsgs * sizeof (char *));
  367.       LOCAL->body = (char **) fs_get (nmsgs * sizeof (char *));
  368.       for (i = 0; i < nmsgs;) {    /* initialize caches */
  369.     LOCAL->header[i] = LOCAL->body[i] = NIL;
  370.                 /* instantiate elt */
  371.     elt = mail_elt (stream,++i);
  372.     elt->valid = elt->recent = T;
  373.       }
  374.       mail_exists(stream,nmsgs);/* notify upper level that messages exist */
  375.       mail_recent (stream,nmsgs);
  376.                 /* notify if empty */
  377.       if (!(nmsgs || stream->silent)) mm_log ("Mailbox is empty",WARN);
  378.     }
  379.     else {            /* error in STAT */
  380.       mm_log (LOCAL->reply,ERROR);
  381.       pop3_close (stream);    /* too bad */
  382.     }
  383.   }
  384.   return LOCAL ? stream : NIL;    /* if stream is alive, return to caller */
  385. }
  386.  
  387. /* POP3 mail close
  388.  * Accepts: MAIL stream
  389.  */
  390.  
  391. void pop3_close (stream)
  392.     MAILSTREAM *stream;
  393. {
  394.   if (LOCAL) {            /* only if a file is open */
  395.     if (LOCAL->tcpstream) {    /* close POP3 connection */
  396.       pop3_send (stream,"QUIT",NIL);
  397.       mm_notify (stream,LOCAL->reply,BYE);
  398.     }
  399.                 /* close POP3 connection */
  400.     if (LOCAL->tcpstream) tcp_close (LOCAL->tcpstream);
  401.     if (LOCAL->host) fs_give ((void **) &LOCAL->host);
  402.     if (LOCAL->response) fs_give ((void **) &LOCAL->response);
  403.                 /* free local scratch buffer */
  404.     if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
  405.     pop3_gc (stream,GC_TEXTS);    /* free local cache */
  406.     if (LOCAL->header) fs_give ((void **) &LOCAL->header);
  407.     if (LOCAL->body) fs_give ((void **) &LOCAL->body);
  408.                 /* nuke the local data */
  409.     fs_give ((void **) &stream->local);
  410.     stream->dtb = NIL;        /* log out the DTB */
  411.   }
  412. }
  413.  
  414. /* POP3 mail fetch fast information
  415.  * Accepts: MAIL stream
  416.  *        sequence
  417.  */
  418.  
  419. void pop3_fetchfast (stream,sequence)
  420.     MAILSTREAM *stream;
  421.     char *sequence;
  422. {
  423.   long i;
  424.   BODY *b;
  425.                 /* ugly and slow */
  426.   if (stream && LOCAL && mail_sequence (stream,sequence))
  427.     for (i = 1; i <= stream->nmsgs; i++)
  428.       if (mail_elt (stream,i)->sequence)
  429.     pop3_fetchstructure (stream,i,&b);
  430. }
  431.  
  432.  
  433. /* POP3 mail fetch flags
  434.  * Accepts: MAIL stream
  435.  *        sequence
  436.  */
  437.  
  438. void pop3_fetchflags (stream,sequence)
  439.     MAILSTREAM *stream;
  440.     char *sequence;
  441. {
  442.   return;            /* no-op for local mail */
  443. }
  444.  
  445. /* POP3 mail fetch envelope
  446.  * Accepts: MAIL stream
  447.  *        message # to fetch
  448.  *        pointer to return body
  449.  * Returns: envelope of this message, body returned in body value
  450.  *
  451.  * Fetches the "fast" information as well
  452.  */
  453.  
  454. ENVELOPE *pop3_fetchstructure (stream,msgno,body)
  455.     MAILSTREAM *stream;
  456.     long msgno;
  457.     BODY **body;
  458. {
  459.   char *h,*t;
  460.   LONGCACHE *lelt;
  461.   ENVELOPE **env;
  462.   STRING bs;
  463.   BODY **b;
  464.   unsigned long hdrsize;
  465.   unsigned long textsize = 0;
  466.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  467.   if (stream->scache) {        /* short cache */
  468.     if (msgno != stream->msgno){/* flush old poop if a different message */
  469.       mail_free_envelope (&stream->env);
  470.       mail_free_body (&stream->body);
  471.     }
  472.     stream->msgno = msgno;
  473.     env = &stream->env;        /* get pointers to envelope and body */
  474.     b = &stream->body;
  475.   }
  476.   else {            /* long cache */
  477.     lelt = mail_lelt (stream,msgno);
  478.     env = &lelt->env;        /* get pointers to envelope and body */
  479.     b = &lelt->body;
  480.   }
  481.   if ((body && !*b) || !*env) {    /* have the poop we need? */
  482.     mail_free_envelope (env);    /* flush old envelope and body */
  483.     mail_free_body (b);
  484.     hdrsize = strlen (h = pop3_fetchheader (stream,msgno));
  485.     if (body) {            /* only if want to parse body */
  486.       textsize = strlen (t = pop3_fetchtext_work (stream,msgno));
  487.                 /* calculate message size */
  488.       elt->rfc822_size = hdrsize + textsize;
  489.       INIT (&bs,mail_string,(void *) t,textsize);
  490.     }
  491.                 /* parse envelope and body */
  492.     rfc822_parse_msg (env,body ? b : NIL,h,hdrsize,body ? &bs : NIL,
  493.               tcp_localhost (LOCAL->tcpstream),LOCAL->buf);
  494.                 /* parse date */
  495.     if (*env && (*env)->date) mail_parse_date (elt,(*env)->date);
  496.     if (!elt->month) mail_parse_date (elt,"01-JAN-1969 00:00:00 GMT");
  497.   }
  498.   if (body) *body = *b;        /* return the body */
  499.   return *env;            /* return the envelope */
  500. }
  501.  
  502. /* POP3 mail fetch message header
  503.  * Accepts: MAIL stream
  504.  *        message # to fetch
  505.  * Returns: message header in RFC822 format
  506.  */
  507.  
  508. char *pop3_fetchheader (stream,msgno)
  509.     MAILSTREAM *stream;
  510.     long msgno;
  511. {
  512.   char *s,*t;
  513.   unsigned long i;
  514.   unsigned long bufpos = 0;
  515.   long m = msgno - 1;
  516.   if (!LOCAL->header[m]) {    /* fetch header if don't have already */
  517.     if (pop3_send_num (stream,"RETR",msgno)) {
  518.       while (s = tcp_getline (LOCAL->tcpstream)) {
  519.     if (*s == '.') {    /* possible end of text? */
  520.       if (s[1]) t = s + 1;    /* pointer to true start of line */
  521.       else break;        /* end of data */
  522.     }
  523.     else t = s;        /* want the entire line */
  524.                 /* ensure have enough room */
  525.     if (LOCAL->buflen < (bufpos + (i = strlen (t)) + 5))
  526.       fs_resize ((void **) &LOCAL->buf,
  527.              LOCAL->buflen += (MAXMESSAGESIZE + 1));
  528.                 /* copy the text */
  529.     strncpy (LOCAL->buf + bufpos,t,i);
  530.     bufpos += i;        /* set new buffer position */
  531.     LOCAL->buf[bufpos++] = '\015';
  532.     LOCAL->buf[bufpos++] = '\012';
  533.                 /* found end of header? */
  534.     if (!(i || LOCAL->header[m])) {
  535.       LOCAL->buf[bufpos++] = '\0';
  536.       LOCAL->header[m] = cpystr (LOCAL->buf);
  537.       bufpos = 0;        /* restart buffer collection */
  538.     }
  539.     fs_give ((void **) &s);    /* free the line */
  540.       }
  541.                 /* add final newline */
  542.       LOCAL->buf[bufpos++] = '\015';
  543.       LOCAL->buf[bufpos++] = '\012';
  544.                 /* tie off string with NUL */
  545.       LOCAL->buf[bufpos++] = '\0';
  546.       if (LOCAL->header[m]) LOCAL->body[m] = cpystr (LOCAL->buf);
  547.       else LOCAL->header[m] = cpystr (LOCAL->buf);
  548.     }
  549.     else mail_elt (stream,msgno)->deleted = T;
  550.   }
  551.   return LOCAL->header[m] ? LOCAL->header[m] : "";
  552. }
  553.  
  554. /* POP3 mail fetch message text (only)
  555.     body only;
  556.  * Accepts: MAIL stream
  557.  *        message # to fetch
  558.  * Returns: message text in RFC822 format
  559.  */
  560.  
  561. char *pop3_fetchtext (stream,msgno)
  562.     MAILSTREAM *stream;
  563.     long msgno;
  564. {
  565.                 /* mark as seen */
  566.   mail_elt (stream,msgno)->seen = T;
  567.   return pop3_fetchtext_work (stream,msgno);
  568. }
  569.  
  570.  
  571. /* POP3 mail fetch message text work routine
  572.  * Accepts: MAIL stream
  573.  *        message # to fetch
  574.  * Returns: message text in RFC822 format
  575.  */
  576.  
  577. char *pop3_fetchtext_work (stream,msgno)
  578.     MAILSTREAM *stream;
  579.     long msgno;
  580. {
  581.   long m = msgno - 1;
  582.                 /* fetch body if don't have already */
  583.   if (!LOCAL->header[m]) pop3_fetchheader (stream,msgno);
  584.   return LOCAL->body[m] ? LOCAL->body[m] : "";
  585. }
  586.  
  587. /* POP3 fetch message body as a structure
  588.  * Accepts: Mail stream
  589.  *        message # to fetch
  590.  *        section specifier
  591.  *        pointer to length
  592.  * Returns: pointer to section of message body
  593.  */
  594.  
  595. char *pop3_fetchbody (stream,m,s,len)
  596.     MAILSTREAM *stream;
  597.     long m;
  598.     char *s;
  599.     unsigned long *len;
  600. {
  601.   BODY *b;
  602.   PART *pt;
  603.   unsigned long i;
  604.   char *base;
  605.   unsigned long offset = 0;
  606.   MESSAGECACHE *elt = mail_elt (stream,m);
  607.                 /* make sure have a body */
  608.   if (!(pop3_fetchstructure (stream,m,&b) && b && s && *s &&
  609.     ((i = strtol (s,&s,10)) > 0) &&
  610.     (base = pop3_fetchtext_work (stream,m))))
  611.     return NIL;
  612.   do {                /* until find desired body part */
  613.                 /* multipart content? */
  614.     if (b->type == TYPEMULTIPART) {
  615.       pt = b->contents.part;    /* yes, find desired part */
  616.       while (--i && (pt = pt->next));
  617.       if (!pt) return NIL;    /* bad specifier */
  618.                 /* note new body, check valid nesting */
  619.       if (((b = &pt->body)->type == TYPEMULTIPART) && !*s) return NIL;
  620.       offset = pt->offset;    /* get new offset */
  621.     }
  622.     else if (i != 1) return NIL;/* otherwise must be section 1 */
  623.                 /* need to go down further? */
  624.     if (i = *s) switch (b->type) {
  625.     case TYPEMESSAGE:        /* embedded message, calculate new base */
  626.       offset = b->contents.msg.offset;
  627.       b = b->contents.msg.body;    /* get its body, drop into multipart case */
  628.     case TYPEMULTIPART:        /* multipart, get next section */
  629.       if ((*s++ == '.') && (i = strtol (s,&s,10)) > 0) break;
  630.     default:            /* bogus subpart specification */
  631.       return NIL;
  632.     }
  633.   } while (i);
  634.                 /* lose if body bogus */
  635.   if ((!b) || b->type == TYPEMULTIPART) return NIL;
  636.   elt->seen = T;        /* mark as seen */
  637.   return rfc822_contents (&LOCAL->buf,&LOCAL->buflen,len,base + offset,
  638.               b->size.ibytes,b->encoding);
  639. }
  640.  
  641. /* POP3 mail set flag
  642.  * Accepts: MAIL stream
  643.  *        sequence
  644.  *        flag(s)
  645.  */
  646.  
  647. void pop3_setflag (stream,sequence,flag)
  648.     MAILSTREAM *stream;
  649.     char *sequence;
  650.     char *flag;
  651. {
  652.   MESSAGECACHE *elt;
  653.   long i;
  654.   short f = pop3_getflags (stream,flag);
  655.   if (!f) return;        /* no-op if no flags to modify */
  656.                 /* get sequence and loop on it */
  657.   if (mail_sequence (stream,sequence)) for (i = 0; i < stream->nmsgs; i++)
  658.     if ((elt = mail_elt (stream,i + 1))->sequence) {
  659.       if (f&fSEEN) elt->seen=T;    /* set all requested flags */
  660.       if (f&fDELETED) {        /* deletion also purges the cache */
  661.     elt->deleted = T;    /* mark deleted */
  662.     if (LOCAL->header[i]) fs_give ((void **) &LOCAL->header[i]);
  663.     LOCAL->body[i] = NIL;
  664.       }
  665.       if (f&fFLAGGED) elt->flagged = T;
  666.       if (f&fANSWERED) elt->answered = T;
  667.     }
  668. }
  669.  
  670.  
  671. /* POP3 mail clear flag
  672.  * Accepts: MAIL stream
  673.  *        sequence
  674.  *        flag(s)
  675.  */
  676.  
  677. void pop3_clearflag (stream,sequence,flag)
  678.     MAILSTREAM *stream;
  679.     char *sequence;
  680.     char *flag;
  681. {
  682.   MESSAGECACHE *elt;
  683.   long i;
  684.   short f = pop3_getflags (stream,flag);
  685.   if (!f) return;        /* no-op if no flags to modify */
  686.                 /* get sequence and loop on it */
  687.   if (mail_sequence (stream,sequence)) for (i = 0; i < stream->nmsgs; i++)
  688.     if ((elt = mail_elt (stream,i + 1))->sequence) {
  689.                 /* clear all requested flags */
  690.       if (f&fSEEN) elt->seen = NIL;
  691.       if (f&fDELETED) elt->deleted = NIL;
  692.       if (f&fFLAGGED) elt->flagged = NIL;
  693.       if (f&fANSWERED) elt->answered = NIL;
  694.     }
  695. }
  696.  
  697. /* POP3 mail search for messages
  698.  * Accepts: MAIL stream
  699.  *        search criteria
  700.  */
  701.  
  702. void pop3_search (stream,criteria)
  703.     MAILSTREAM *stream;
  704.     char *criteria;
  705. {
  706.   long i,n;
  707.   char *d;
  708.   search_t f;
  709.                 /* initially all searched */
  710.   for (i = 1; i <= stream->nmsgs; ++i) mail_elt (stream,i)->searched = T;
  711.                 /* get first criterion */
  712.   if (criteria && (criteria = strtok (criteria," "))) {
  713.                 /* for each criterion */
  714.     for (; criteria; (criteria = strtok (NIL," "))) {
  715.       f = NIL; d = NIL; n = 0;    /* init then scan the criterion */
  716.       switch (*ucase (criteria)) {
  717.       case 'A':            /* possible ALL, ANSWERED */
  718.     if (!strcmp (criteria+1,"LL")) f = pop3_search_all;
  719.     else if (!strcmp (criteria+1,"NSWERED")) f = pop3_search_answered;
  720.     break;
  721.       case 'B':            /* possible BCC, BEFORE, BODY */
  722.     if (!strcmp (criteria+1,"CC"))
  723.       f = pop3_search_string (pop3_search_bcc,&d,&n);
  724.     else if (!strcmp (criteria+1,"EFORE"))
  725.       f = pop3_search_date (pop3_search_before,&n);
  726.     else if (!strcmp (criteria+1,"ODY"))
  727.       f = pop3_search_string (pop3_search_body,&d,&n);
  728.     break;
  729.       case 'C':            /* possible CC */
  730.     if (!strcmp (criteria+1,"C"))
  731.       f = pop3_search_string (pop3_search_cc,&d,&n);
  732.     break;
  733.       case 'D':            /* possible DELETED */
  734.     if (!strcmp (criteria+1,"ELETED")) f = pop3_search_deleted;
  735.     break;
  736.       case 'F':            /* possible FLAGGED, FROM */
  737.     if (!strcmp (criteria+1,"LAGGED")) f = pop3_search_flagged;
  738.     else if (!strcmp (criteria+1,"ROM"))
  739.       f = pop3_search_string (pop3_search_from,&d,&n);
  740.     break;
  741.       case 'K':            /* possible KEYWORD */
  742.     if (!strcmp (criteria+1,"EYWORD"))
  743.       f = pop3_search_flag (pop3_search_keyword,&d);
  744.     break;
  745.       case 'N':            /* possible NEW */
  746.     if (!strcmp (criteria+1,"EW")) f = pop3_search_new;
  747.     break;
  748.  
  749.       case 'O':            /* possible OLD, ON */
  750.     if (!strcmp (criteria+1,"LD")) f = pop3_search_old;
  751.     else if (!strcmp (criteria+1,"N"))
  752.       f = pop3_search_date (pop3_search_on,&n);
  753.     break;
  754.       case 'R':            /* possible RECENT */
  755.     if (!strcmp (criteria+1,"ECENT")) f = pop3_search_recent;
  756.     break;
  757.       case 'S':            /* possible SEEN, SINCE, SUBJECT */
  758.     if (!strcmp (criteria+1,"EEN")) f = pop3_search_seen;
  759.     else if (!strcmp (criteria+1,"INCE"))
  760.       f = pop3_search_date (pop3_search_since,&n);
  761.     else if (!strcmp (criteria+1,"UBJECT"))
  762.       f = pop3_search_string (pop3_search_subject,&d,&n);
  763.     break;
  764.       case 'T':            /* possible TEXT, TO */
  765.     if (!strcmp (criteria+1,"EXT"))
  766.       f = pop3_search_string (pop3_search_text,&d,&n);
  767.     else if (!strcmp (criteria+1,"O"))
  768.       f = pop3_search_string (pop3_search_to,&d,&n);
  769.     break;
  770.       case 'U':            /* possible UN* */
  771.     if (criteria[1] == 'N') {
  772.       if (!strcmp (criteria+2,"ANSWERED")) f = pop3_search_unanswered;
  773.       else if (!strcmp (criteria+2,"DELETED")) f = pop3_search_undeleted;
  774.       else if (!strcmp (criteria+2,"FLAGGED")) f = pop3_search_unflagged;
  775.       else if (!strcmp (criteria+2,"KEYWORD"))
  776.         f = pop3_search_flag (pop3_search_unkeyword,&d);
  777.       else if (!strcmp (criteria+2,"SEEN")) f = pop3_search_unseen;
  778.     }
  779.     break;
  780.       default:            /* we will barf below */
  781.     break;
  782.       }
  783.       if (!f) {            /* if can't determine any criteria */
  784.     sprintf (LOCAL->buf,"Unknown search criterion: %.80s",criteria);
  785.     mm_log (LOCAL->buf,ERROR);
  786.     return;
  787.       }
  788.                 /* run the search criterion */
  789.       for (i = 1; i <= stream->nmsgs; ++i)
  790.     if (mail_elt (stream,i)->searched && !(*f) (stream,i,d,n))
  791.       mail_elt (stream,i)->searched = NIL;
  792.     }
  793.                 /* report search results to main program */
  794.     for (i = 1; i <= stream->nmsgs; ++i)
  795.       if (mail_elt (stream,i)->searched) mail_searched (stream,i);
  796.   }
  797. }
  798.  
  799. /* POP3 mail ping mailbox
  800.  * Accepts: MAIL stream
  801.  * Returns: T if stream alive, else NIL
  802.  */
  803.  
  804. long pop3_ping (stream)
  805.     MAILSTREAM *stream;
  806. {
  807.   return pop3_send (stream,"NOOP",NIL);
  808. }
  809.  
  810.  
  811. /* POP3 mail check mailbox
  812.  * Accepts: MAIL stream
  813.  */
  814.  
  815. void pop3_check (stream)
  816.     MAILSTREAM *stream;
  817. {
  818.   if (pop3_ping (stream)) mm_log ("Check completed",NIL);
  819. }
  820.  
  821. /* POP3 mail expunge mailbox
  822.  * Accepts: MAIL stream
  823.  */
  824.  
  825. void pop3_expunge (stream)
  826.     MAILSTREAM *stream;
  827. {
  828.   MESSAGECACHE *elt;
  829.   unsigned long i,n = 0;
  830.   for (i = 1; i <= stream->nmsgs; i++)
  831.     if ((elt = mail_elt (stream,i))->deleted && !elt->data1 &&
  832.     (elt->data1 = pop3_send_num (stream,"DELE",i))) n++;
  833.   if (!stream->silent) {    /* only if not silent */
  834.     if (n) {            /* did we expunge anything? */
  835.       sprintf (LOCAL->buf,"Expunged %ld messages",n);
  836.       mm_log (LOCAL->buf,(long) NIL);
  837.     }
  838.     else mm_log ("No messages deleted, so no update needed",(long) NIL);
  839.   }
  840. }
  841.  
  842.  
  843. /* POP3 mail copy message(s)
  844.     s;
  845.  * Accepts: MAIL stream
  846.  *        sequence
  847.  *        destination mailbox
  848.  * Returns: T if copy successful, else NIL
  849.  */
  850.  
  851. long pop3_copy (stream,sequence,mailbox)
  852.     MAILSTREAM *stream;
  853.     char *sequence;
  854.     char *mailbox;
  855. {
  856.   mm_log ("Copy not valid for POP3",ERROR);
  857.   return NIL;
  858. }
  859.  
  860.  
  861. /* POP3 mail move message(s)
  862.     s;
  863.  * Accepts: MAIL stream
  864.  *        sequence
  865.  *        destination mailbox
  866.  * Returns: T if move successful, else NIL
  867.  */
  868.  
  869. long pop3_move (stream,sequence,mailbox)
  870.     MAILSTREAM *stream;
  871.     char *sequence;
  872.     char *mailbox;
  873. {
  874.   mm_log ("Move not valid for POP3",ERROR);
  875.   return NIL;
  876. }
  877.  
  878.  
  879. /* POP3 mail append message from stringstruct
  880.  * Accepts: MAIL stream
  881.  *        destination mailbox
  882.  *        stringstruct of messages to append
  883.  * Returns: T if append successful, else NIL
  884.  */
  885.  
  886. long pop3_append (stream,mailbox,flags,date,message)
  887.     MAILSTREAM *stream;
  888.     char *mailbox;
  889.     char *flags;
  890.     char *date;
  891.                STRING *message;
  892. {
  893.   mm_log ("Append not valid for POP3",ERROR);
  894.   return NIL;
  895. }
  896.  
  897. /* POP3 garbage collect stream
  898.  * Accepts: Mail stream
  899.  *        garbage collection flags
  900.  */
  901.  
  902. void pop3_gc (stream,gcflags)
  903.     MAILSTREAM *stream;
  904.     long gcflags;
  905. {
  906.   unsigned long i;
  907.   if (!stream->halfopen)     /* never on half-open stream */
  908.     if (gcflags & GC_TEXTS)    /* garbage collect texts? */
  909.                 /* flush texts from cache */
  910.       for (i = 0; i < stream->nmsgs; i++) {
  911.     if (LOCAL->header[i]) fs_give ((void **) &LOCAL->header[i]);
  912.     LOCAL->body[i] = NIL;
  913.       }
  914. }
  915.  
  916. /* Internal routines */
  917.  
  918.  
  919. /* Post Office Protocol 3 send command with number argument
  920.  * Accepts: MAIL stream
  921.  *        command
  922.  *        number
  923.  * Returns: T if successful, NIL if failure
  924.  */
  925.  
  926. long pop3_send_num (stream,command,n)
  927.     MAILSTREAM *stream;
  928.     char *command;
  929.     unsigned long n;
  930. {
  931.   char tmp[MAILTMPLEN];
  932.   sprintf (tmp,"%ld",n);
  933.   return pop3_send (stream,command,tmp);
  934. }
  935.  
  936.  
  937. /* Post Office Protocol 3 send command
  938.  * Accepts: MAIL stream
  939.  *        command
  940.  *        command argument
  941.  * Returns: T if successful, NIL if failure
  942.  */
  943.  
  944. long pop3_send (stream,command,args)
  945.     MAILSTREAM *stream;
  946.     char *command;
  947.     char *args;
  948. {
  949.   char tmp[MAILTMPLEN];
  950.   if (!LOCAL->tcpstream) return pop3_fake (stream,"No-op dead stream");
  951.                 /* build the complete command */
  952.   if (args) sprintf (tmp,"%s %s",command,args);
  953.   else strcpy (tmp,command);
  954.   if (stream->debug) mm_dlog (tmp);
  955.   strcat (tmp,"\015\012");
  956.                 /* send the command */
  957.   return tcp_soutr (LOCAL->tcpstream,tmp) ? pop3_reply (stream) :
  958.     pop3_fake (stream,"POP3 connection broken in command");
  959. }
  960.  
  961. /* Post Office Protocol 3 get reply
  962.  * Accepts: MAIL stream
  963.  * Returns: T if success reply, NIL if error reply
  964.  */
  965.  
  966. long pop3_reply (stream)
  967.     MAILSTREAM *stream;
  968. {
  969.   char *s;
  970.   if (!LOCAL->tcpstream) return pop3_fake (stream,"No-op dead stream");
  971.                 /* flush old reply */
  972.   if (LOCAL->response) fs_give ((void **) &LOCAL->response);
  973.                   /* get reply */
  974.   if (!(LOCAL->response = tcp_getline (LOCAL->tcpstream)))
  975.     return pop3_fake (stream,"POP3 connection broken in response");
  976.   if (stream->debug) mm_dlog (LOCAL->response);
  977.   LOCAL->reply = (s = strchr (LOCAL->response,' ')) ? s + 1 : LOCAL->response;
  978.                 /* return success or failure */
  979.   return (*LOCAL->response =='+') ? T : NIL;
  980. }
  981.  
  982.  
  983. /* Post Office Protocol 3 set fake error
  984.  * Accepts: MAIL stream
  985.  *        error text
  986.  * Returns: NIL, always
  987.  */
  988.  
  989. long pop3_fake (stream,text)
  990.     MAILSTREAM *stream;
  991.     char *text;
  992. {
  993.   mm_notify (stream,text,BYE);    /* send bye alert */
  994.   if (LOCAL->tcpstream) tcp_close (LOCAL->tcpstream);
  995.   LOCAL->tcpstream = NIL;    /* farewell, dear TCP stream */
  996.                 /* flush any old reply */
  997.   if (LOCAL->response) fs_give ((void **) &LOCAL->response);
  998.   LOCAL->reply = text;        /* set up pseudo-reply string */
  999.   return NIL;            /* return error code */
  1000. }
  1001.  
  1002.  
  1003. /* Parse flag list
  1004.  * Accepts: MAIL stream
  1005.  *        flag list as a character string
  1006.  * Returns: flag command list
  1007.  */
  1008.  
  1009. short pop3_getflags (stream,flag)
  1010.     MAILSTREAM *stream;
  1011.     char *flag;
  1012. {
  1013.   char *t,tmp[MAILTMPLEN],err[MAILTMPLEN];
  1014.   short f = 0;
  1015.   short i,j;
  1016.   if (flag && *flag) {        /* no-op if no flag string */
  1017.                 /* check if a list and make sure valid */
  1018.     if ((i = (*flag == '(')) ^ (flag[strlen (flag)-1] == ')')) {
  1019.       mm_log ("Bad flag list",ERROR);
  1020.       return NIL;
  1021.     }
  1022.                 /* copy the flag string w/o list construct */
  1023.     strncpy (tmp,flag+i,(j = strlen (flag) - (2*i)));
  1024.     tmp[j] = '\0';
  1025.     t = ucase (tmp);        /* uppercase only from now on */
  1026.  
  1027.     while (t && *t) {        /* parse the flags */
  1028.       if (*t == '\\') {        /* system flag? */
  1029.     switch (*++t) {        /* dispatch based on first character */
  1030.     case 'S':        /* possible \Seen flag */
  1031.       if (t[1] == 'E' && t[2] == 'E' && t[3] == 'N') i = fSEEN;
  1032.       t += 4;        /* skip past flag name */
  1033.       break;
  1034.     case 'D':        /* possible \Deleted flag */
  1035.       if (t[1] == 'E' && t[2] == 'L' && t[3] == 'E' && t[4] == 'T' &&
  1036.           t[5] == 'E' && t[6] == 'D') i = fDELETED;
  1037.       t += 7;        /* skip past flag name */
  1038.       break;
  1039.     case 'F':        /* possible \Flagged flag */
  1040.       if (t[1] == 'L' && t[2] == 'A' && t[3] == 'G' && t[4] == 'G' &&
  1041.           t[5] == 'E' && t[6] == 'D') i = fFLAGGED;
  1042.       t += 7;        /* skip past flag name */
  1043.       break;
  1044.     case 'A':        /* possible \Answered flag */
  1045.       if (t[1] == 'N' && t[2] == 'S' && t[3] == 'W' && t[4] == 'E' &&
  1046.           t[5] == 'R' && t[6] == 'E' && t[7] == 'D') i = fANSWERED;
  1047.       t += 8;        /* skip past flag name */
  1048.       break;
  1049.     default:        /* unknown */
  1050.       i = 0;
  1051.       break;
  1052.     }
  1053.                 /* add flag to flags list */
  1054.     if (i && ((*t == '\0') || (*t++ == ' '))) f |= i;
  1055.       }
  1056.       else {            /* no user flags yet */
  1057.     t = strtok (t," ");    /* isolate flag name */
  1058.     sprintf (err,"Unknown flag: %.80s",t);
  1059.     t = strtok (NIL," ");    /* get next flag */
  1060.     mm_log (err,ERROR);
  1061.       }
  1062.     }
  1063.   }
  1064.   return f;
  1065. }
  1066.  
  1067. /* Search support routines
  1068.  * Accepts: MAIL stream
  1069.  *        message number
  1070.  *        pointer to additional data
  1071.  *        pointer to temporary buffer
  1072.  * Returns: T if search matches, else NIL
  1073.  */
  1074.  
  1075. char pop3_search_all (stream,msgno,d,n)
  1076.     MAILSTREAM *stream;
  1077.     long msgno;
  1078.     char *d;
  1079.     long n;
  1080. {
  1081.   return T;            /* ALL always succeeds */
  1082. }
  1083.  
  1084.  
  1085. char pop3_search_answered (stream,msgno,d,n)
  1086.     MAILSTREAM *stream;
  1087.     long msgno;
  1088.     char *d;
  1089.     long n;
  1090. {
  1091.   return mail_elt (stream,msgno)->answered ? T : NIL;
  1092. }
  1093.  
  1094.  
  1095. char pop3_search_deleted (stream,msgno,d,n)
  1096.     MAILSTREAM *stream;
  1097.     long msgno;
  1098.     char *d;
  1099.     long n;
  1100. {
  1101.   return mail_elt (stream,msgno)->deleted ? T : NIL;
  1102. }
  1103.  
  1104.  
  1105. char pop3_search_flagged (stream,msgno,d,n)
  1106.     MAILSTREAM *stream;
  1107.     long msgno;
  1108.     char *d;
  1109.     long n;
  1110. {
  1111.   return mail_elt (stream,msgno)->flagged ? T : NIL;
  1112. }
  1113.  
  1114.  
  1115. char pop3_search_keyword (stream,msgno,d,n)
  1116.     MAILSTREAM *stream;
  1117.     long msgno;
  1118.     char *d;
  1119.     long n;
  1120. {
  1121.   return NIL;            /* keywords not supported yet */
  1122. }
  1123.  
  1124.  
  1125. char pop3_search_new (stream,msgno,d,n)
  1126.     MAILSTREAM *stream;
  1127.     long msgno;
  1128.     char *d;
  1129.     long n;
  1130. {
  1131.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1132.   return (elt->recent && !elt->seen) ? T : NIL;
  1133. }
  1134.  
  1135. char pop3_search_old (stream,msgno,d,n)
  1136.     MAILSTREAM *stream;
  1137.     long msgno;
  1138.     char *d;
  1139.     long n;
  1140. {
  1141.   return mail_elt (stream,msgno)->recent ? NIL : T;
  1142. }
  1143.  
  1144.  
  1145. char pop3_search_recent (stream,msgno,d,n)
  1146.     MAILSTREAM *stream;
  1147.     long msgno;
  1148.     char *d;
  1149.     long n;
  1150. {
  1151.   return mail_elt (stream,msgno)->recent ? T : NIL;
  1152. }
  1153.  
  1154.  
  1155. char pop3_search_seen (stream,msgno,d,n)
  1156.     MAILSTREAM *stream;
  1157.     long msgno;
  1158.     char *d;
  1159.     long n;
  1160. {
  1161.   return mail_elt (stream,msgno)->seen ? T : NIL;
  1162. }
  1163.  
  1164.  
  1165. char pop3_search_unanswered (stream,msgno,d,n)
  1166.     MAILSTREAM *stream;
  1167.     long msgno;
  1168.     char *d;
  1169.     long n;
  1170. {
  1171.   return mail_elt (stream,msgno)->answered ? NIL : T;
  1172. }
  1173.  
  1174.  
  1175. char pop3_search_undeleted (stream,msgno,d,n)
  1176.     MAILSTREAM *stream;
  1177.     long msgno;
  1178.     char *d;
  1179.     long n;
  1180. {
  1181.   return mail_elt (stream,msgno)->deleted ? NIL : T;
  1182. }
  1183.  
  1184.  
  1185. char pop3_search_unflagged (stream,msgno,d,n)
  1186.     MAILSTREAM *stream;
  1187.     long msgno;
  1188.     char *d;
  1189.     long n;
  1190. {
  1191.   return mail_elt (stream,msgno)->flagged ? NIL : T;
  1192. }
  1193.  
  1194.  
  1195. char pop3_search_unkeyword (stream,msgno,d,n)
  1196.     MAILSTREAM *stream;
  1197.     long msgno;
  1198.     char *d;
  1199.     long n;
  1200. {
  1201.   return T;            /* keywords not supported yet */
  1202. }
  1203.  
  1204.  
  1205. char pop3_search_unseen (stream,msgno,d,n)
  1206.     MAILSTREAM *stream;
  1207.     long msgno;
  1208.     char *d;
  1209.     long n;
  1210. {
  1211.   return mail_elt (stream,msgno)->seen ? NIL : T;
  1212. }
  1213.  
  1214. char pop3_search_before (stream,msgno,d,n)
  1215.     MAILSTREAM *stream;
  1216.     long msgno;
  1217.     char *d;
  1218.     long n;
  1219. {
  1220.   return (char) (pop3_msgdate (stream,msgno) < n);
  1221. }
  1222.  
  1223.  
  1224. char pop3_search_on (stream,msgno,d,n)
  1225.     MAILSTREAM *stream;
  1226.     long msgno;
  1227.     char *d;
  1228.     long n;
  1229. {
  1230.   return (char) (pop3_msgdate (stream,msgno) == n);
  1231. }
  1232.  
  1233.  
  1234. char pop3_search_since (stream,msgno,d,n)
  1235.     MAILSTREAM *stream;
  1236.     long msgno;
  1237.     char *d;
  1238.     long n;
  1239. {
  1240.                 /* everybody interprets "since" as .GE. */
  1241.   return (char) (pop3_msgdate (stream,msgno) >= n);
  1242. }
  1243.  
  1244.  
  1245. unsigned long pop3_msgdate (stream,msgno)
  1246.     MAILSTREAM *stream;
  1247.     long msgno;
  1248. {
  1249.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1250.                 /* get date if don't have it yet */
  1251.   if (!elt->day) pop3_fetchstructure (stream,msgno,NIL);
  1252.   return (long) (elt->year << 9) + (elt->month << 5) + elt->day;
  1253. }
  1254.  
  1255.  
  1256. char pop3_search_body (stream,msgno,d,n)
  1257.     MAILSTREAM *stream;
  1258.     long msgno;
  1259.     char *d;
  1260.     long n;
  1261. {
  1262.   char *t = pop3_fetchtext_work (stream,msgno);
  1263.   return (t && search (t,(unsigned long) strlen (t),d,n));
  1264. }
  1265.  
  1266.  
  1267. char pop3_search_subject (stream,msgno,d,n)
  1268.     MAILSTREAM *stream;
  1269.     long msgno;
  1270.     char *d;
  1271.     long n;
  1272. {
  1273.   char *t = pop3_fetchstructure (stream,msgno,NIL)->subject;
  1274.   return t ? search (t,strlen (t),d,n) : NIL;
  1275. }
  1276.  
  1277.  
  1278. char pop3_search_text (stream,msgno,d,n)
  1279.     MAILSTREAM *stream;
  1280.     long msgno;
  1281.     char *d;
  1282.     long n;
  1283. {
  1284.   char *t = pop3_fetchheader (stream,msgno);
  1285.   return (t && search (t,strlen (t),d,n)) ||
  1286.     pop3_search_body (stream,msgno,d,n);
  1287. }
  1288.  
  1289. char pop3_search_bcc (stream,msgno,d,n)
  1290.     MAILSTREAM *stream;
  1291.     long msgno;
  1292.     char *d;
  1293.     long n;
  1294. {
  1295.   ADDRESS *a = pop3_fetchstructure (stream,msgno,NIL)->bcc;
  1296.   LOCAL->buf[0] = '\0';        /* initially empty string */
  1297.                 /* get text for address */
  1298.   rfc822_write_address (LOCAL->buf,a);
  1299.   return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
  1300. }
  1301.  
  1302.  
  1303. char pop3_search_cc (stream,msgno,d,n)
  1304.     MAILSTREAM *stream;
  1305.     long msgno;
  1306.     char *d;
  1307.     long n;
  1308. {
  1309.   ADDRESS *a = pop3_fetchstructure (stream,msgno,NIL)->cc;
  1310.   LOCAL->buf[0] = '\0';        /* initially empty string */
  1311.                 /* get text for address */
  1312.   rfc822_write_address (LOCAL->buf,a);
  1313.   return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
  1314. }
  1315.  
  1316.  
  1317. char pop3_search_from (stream,msgno,d,n)
  1318.     MAILSTREAM *stream;
  1319.     long msgno;
  1320.     char *d;
  1321.     long n;
  1322. {
  1323.   ADDRESS *a = pop3_fetchstructure (stream,msgno,NIL)->from;
  1324.   LOCAL->buf[0] = '\0';        /* initially empty string */
  1325.                 /* get text for address */
  1326.   rfc822_write_address (LOCAL->buf,a);
  1327.   return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
  1328. }
  1329.  
  1330.  
  1331. char pop3_search_to (stream,msgno,d,n)
  1332.     MAILSTREAM *stream;
  1333.     long msgno;
  1334.     char *d;
  1335.     long n;
  1336. {
  1337.   ADDRESS *a = pop3_fetchstructure (stream,msgno,NIL)->to;
  1338.   LOCAL->buf[0] = '\0';        /* initially empty string */
  1339.                 /* get text for address */
  1340.   rfc822_write_address (LOCAL->buf,a);
  1341.   return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
  1342. }
  1343.  
  1344. /* Search parsers */
  1345.  
  1346.  
  1347. /* Parse a date
  1348.  * Accepts: function to return
  1349.  *        pointer to date integer to return
  1350.  * Returns: function to return
  1351.  */
  1352.  
  1353. search_t pop3_search_date (f,n)
  1354.     search_t f;
  1355.     long *n;
  1356. {
  1357.   long i;
  1358.   char *s;
  1359.   MESSAGECACHE elt;
  1360.                 /* parse the date and return fn if OK */
  1361.   return (pop3_search_string (f,&s,&i) && mail_parse_date (&elt,s) &&
  1362.       (*n = (elt.year << 9) + (elt.month << 5) + elt.day)) ? f : NIL;
  1363. }
  1364.  
  1365. /* Parse a flag
  1366.  * Accepts: function to return
  1367.  *        pointer to string to return
  1368.  * Returns: function to return
  1369.  */
  1370.  
  1371. search_t pop3_search_flag (f,d)
  1372.     search_t f;
  1373.     char **d;
  1374. {
  1375.                 /* get a keyword, return if OK */
  1376.   return (*d = strtok (NIL," ")) ? f : NIL;
  1377. }
  1378.  
  1379.  
  1380. /* Parse a string
  1381.  * Accepts: function to return
  1382.  *        pointer to string to return
  1383.  *        pointer to string length to return
  1384.  * Returns: function to return
  1385.  */
  1386.  
  1387. search_t pop3_search_string (f,d,n)
  1388.     search_t f;
  1389.     char **d;
  1390.     long *n;
  1391. {
  1392.   char *end = " ";
  1393.   char *c = strtok (NIL,"");    /* remainder of criteria */
  1394.   if (!c) return NIL;        /* missing argument */
  1395.   switch (*c) {            /* see what the argument is */
  1396.   case '{':            /* literal string */
  1397.     *n = strtol (c+1,d,10);    /* get its length */
  1398.     if ((*(*d)++ == '}') && (*(*d)++ == '\015') && (*(*d)++ == '\012') &&
  1399.     (!(*(c = *d + *n)) || (*c == ' '))) {
  1400.       char e = *--c;
  1401.       *c = DELIM;        /* make sure not a space */
  1402.       strtok (c," ");        /* reset the strtok mechanism */
  1403.       *c = e;            /* put character back */
  1404.       break;
  1405.     }
  1406.   case '\0':            /* catch bogons */
  1407.   case ' ':
  1408.     return NIL;
  1409.   case '"':            /* quoted string */
  1410.     if (strchr (c+1,'"')) end = "\"";
  1411.     else return NIL;
  1412.   default:            /* atomic string */
  1413.     if (*d = strtok (c,end)) *n = strlen (*d);
  1414.     else return NIL;
  1415.     break;
  1416.   }
  1417.   return f;
  1418. }
  1419.